package com.swapper.math.fsa;

import java.util.*;

/**
 * 状态转换图、状态转换表中的状态
 */
public final class State {
  private final int id;
  /**
   * 是否是初始状态
   */
  private final boolean isInitial;
  /**
   * 是否是终结状态
   */
  private final boolean isFinally;
  private final Map<String, Set<State>> map = new HashMap<>();

  private static final Comparator<State> COMPARING = Comparator.comparing(s -> s.id);

  public static Set<State> createGroup() {
    return new TreeSet<>(COMPARING);
  }

  public static Set<State> shift(Set<State> states, String input) {
    Set<State> group = new TreeSet<>(COMPARING);
    for (State state : states) {
      group.addAll(state.shift(input));
    }
    return group;
  }

  public State(int id) {
    this(id, false, false);
  }

  public State(int id, boolean isInitial, boolean isFinally) {
    this.id = id;
    this.isInitial = isInitial;
    this.isFinally = isFinally;
  }

  public int getId() {
    return id;
  }

  public boolean isInitial() {
    return isInitial;
  }

  public boolean isFinally() {
    return isFinally;
  }

  /**
   * 当前状态的输入集
   */
  public Set<String> getInputs() {
    return map.keySet();
  }

  /**
   * 设置当前状态的转换
   *
   * @param input   输入
   * @param targets 响应状态
   */
  public void translation(String input, Set<State> targets) {
    if (input == null) {
      throw new IllegalArgumentException("input is null.");
    }
    if (targets == null) {
      throw new IllegalArgumentException("targets is null.");
    }
    Set<State> value;
    if (map.containsKey(input)) {
      value = map.get(input);
    } else {
      value = createGroup();
    }
    value.addAll(targets);
    map.put(input, value);
  }

  /**
   * 当前状态的epsilon闭包
   */
  public Set<State> epsilonClosure() {
    Set<State> group = createGroup();
    epsilonClosureInner(group);
    return group;
  }

  private void epsilonClosureInner(Set<State> group) {
    if (!group.contains(this)) {
      group.add(this);
      Set<State> states = map.get("");
      if (states != null && !states.isEmpty()) {
        for (State state : states) {
          state.epsilonClosureInner(group);
        }
      }
    }
  }

  public Set<State> shift(String input) {
    if (input == null) {
      throw new IllegalArgumentException("input is null.");
    }
    Set<State> group = createGroup();
    Set<State> epsilonClosure = epsilonClosure();
    for (State state1 : epsilonClosure) {
      Set<State> directStates = state1.map.get(input);
      if (directStates != null && !directStates.isEmpty()) {
        group.addAll(directStates);
        for (State state2 : directStates) {
          group.addAll(state2.epsilonClosure());
        }
      }
    }
    return group;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    State state = (State) o;
    return id == state.id;
  }

  @Override
  public int hashCode() {
    return id;
  }

  @Override
  public String toString() {
    return String.format("%s%d%s", isInitial ? "#" : "", id, isFinally ? "#" : "");
  }
}
